﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Web;
using System.Text.RegularExpressions;
using System.Drawing;

namespace VitalyZayko.Code4Blog
{
    class HtmlConverter
    {
        private const string beginstring = "<!-- Start block. Created with Code4Blog for Microsoft Visual Studio 2010. Copyright (c)2010 Vitaly Zayko http://zayko.net -->\r\n";
        private const string endstring = "\r\n<!-- End block -->";

        private bool _ShowBorder;
        private bool _LineNumbers;
        private decimal _MaxWidth;
        private decimal _MaxHeight;
        private string _EvenColor;
        private string _OddColor;
        private string _FontName;
        private decimal _FontSize;

        public HtmlConverter(string EvenColor, string OddColor, bool ShowBorder = false, bool LineNumbers = false,
            decimal MaxWidth = 0, decimal MaxHeight = 0, string FontName = "", decimal FontSize = 0)
        {
            _ShowBorder = ShowBorder;
            _LineNumbers = LineNumbers;
            _MaxWidth = MaxWidth;
            _MaxHeight = MaxHeight;
            _EvenColor = EvenColor;
            _OddColor = OddColor;
            _FontName = FontName;
            _FontSize = FontSize;
        }

        public string GetHtmlFromClipboard()
        {
            if( !Clipboard.ContainsData(DataFormats.Rtf) )
                throw new ArgumentOutOfRangeException("Clipboard doesn't contain RTF!");

            string rtf = HttpUtility.HtmlEncode(Clipboard.GetData(DataFormats.Rtf).ToString());
            Clipboard.Clear();

            // Extract color table:
            Dictionary<string, string> HtmlColors = new Dictionary<string, string>();

            Match m = Regex.Match(rtf, @"\{\\colortbl;.+\;}");
            if( m.Success )
            {
                string[] clrs = m.Value.Split(new char[] { ';' });
                for( int i = 1; i < clrs.Length - 1; i++ )
                {
                    string[] colors = Regex.Split(clrs[i], @"\\red(?<red>\d+)\\green(?<green>\d+)\\blue(?<blue>\d+)");
                    if( colors.Length != 5 )
                        throw new ArgumentException();

                    HtmlColors.Add(String.Format("{0}", i), ConvertHtmlColors(colors));
                }

            }
            else
                throw new ArgumentException("RTF file is broken!");

            // Remove the header:
            string text = Regex.Replace(rtf, @"\{.+\}\}\{.+;\}", String.Empty);

            // Remove last bracket:
            text = text.Remove(text.Length - 1);

            // Remove font info:
            text = Regex.Replace(text, @"\\f\d+ \\fs\d+", String.Empty);

            // Replace colors:
            foreach( string clr in HtmlColors.Keys )
            {
                text = text.Replace(String.Format("\\cf{0} ", clr), String.Format("<span style=\"color:{0}\">", HtmlColors[clr]));
                text = text.Replace(String.Format("\\cb{0} ", clr), String.Format("<span style=\"background:{0}\">", HtmlColors[clr]));
            }

            // Remove highlight* and close background span:
            //ikarstein: BEGIN
            //  Inserted this line because trailing white spaces after RTF control sequences have to be removed. 
            text = text.Replace("\\cb0 ", "<span style=\"background:#FFFFFF;\">"); //ikarstein: WHITE ist used as "default" background color.
            //  But the following line of code (replace control sequences *without* trailing white space) is needed too!
            //ikarstein: END

            text = text.Replace("\\cb0", "<span style=\"background:#FFFFFF;\">"); //ikarstein: WHITE ist used as "default" background color.
            text = Regex.Replace(text, @"\\highlight\d", String.Empty);

            //text = Regex.Replace(text, @"\s*(\w*|\W*)\\uinput\d+\\u", "&#");
            text = Regex.Replace(text, @"\\uinput\d+\\u", "&#");
            text = Regex.Replace(text, @"\s*\S*&#", "&#");

            // Replace cf0:
            //ikarstein: BEGIN
            //  Inserted this line because trailing white spaces after RTF control sequences have to be removed. 
            text = text.Replace("\\cf0 ", "<span style=\"color:#000000;\">"); //ikarstein: BLACK ist used as "default" background color.
            //  But the following line of code (replace control sequences *without* trailing white space) is needed too!
            //ikarstein: END
            text = text.Replace("\\cf0", "<span style=\"color:#000000;\">"); //ikarstein: BLACK ist used as "default" background color.

            // Remove slash before bracket:
            text = text.Replace("\\{", "<span style=\"color:#000000\">{</span>").Replace("\\}", "<span style=\"color:#000000\">}</span>");

            IList<string> lines = text.Split(new string[] { @"\par" }, StringSplitOptions.None).ToList<string>();

            // Line number:
            int lineNo = 1;
            // Final buffer:
            StringBuilder res = new StringBuilder();

            foreach( string s in lines )
            {
                // Line background:
                string bkg;
                if( ( lineNo & 1 ) == 0 )
                    bkg = _EvenColor;
                else
                    bkg = _OddColor;
                if( String.IsNullOrWhiteSpace(bkg) )
                    res.Append("<pre style=\"margin:0em;\">");
                else
                    res.Append(String.Format("<pre style=\"background:{0};margin:0em;\">", bkg));

                if( _LineNumbers )
                    res.Append(String.Format("<span style=\"text-align:right;display:inline-block;width:30px;margin-right:10px;color:black\">{0}:</span>", lineNo));

                //ikarstein: BEGIN
                // Bug Fix by ikarstein, 2011/04/07
                // The code above produces many "<span>" tags *without* their closing tags!!! So the HTML code is
                // invalid. - The snippet below just counts the SPANs and adds the same number of /SPANs before the closing PRE tag
                // This have to be done per line (<PRE>...</PRE>).

                int countSPANs = 0;
                int posInString = 0;
                do
                {
                    posInString = s.IndexOf("<span", posInString, StringComparison.InvariantCultureIgnoreCase);
                    if( posInString > -1 )
                    {
                        countSPANs++;
                        posInString += 5;
                    }
                } while( posInString > -1 && posInString < s.Length );
    
                //...decrement by the number of /SPANs already in the code.
                posInString = 0;
                do
                {
                    posInString = s.IndexOf("</span", posInString, StringComparison.InvariantCultureIgnoreCase);
                    if( posInString > -1 )
                    {
                        countSPANs--;
                        posInString += 6;
                    }
                } while( posInString > -1 && posInString < s.Length );

                res.Append(s);
                for( int i = 0; i < countSPANs; i++ )
                    res.Append("</span>");

                //ikarstein: END

                res.Append("</pre>\r\n");

                lineNo++;
            }


            // Additional styles (border, with/height, font):
            StringBuilder style = new StringBuilder("<div style=\"color:black;overflow:auto;");

            // Font name:
            if( !String.IsNullOrWhiteSpace(_FontName) )
            {
                style.Append(String.Format("font-family:{0};", _FontName));

                // Font size:
                if( _FontSize > 0 )
                    style.Append(String.Format("font-size:{0}pt;", (int)_FontSize));
            }

            // Border:
            if( _ShowBorder )
                style.Append("border: 1px solid black;padding:5px;");

            // Max size:
            if( _MaxWidth > 0 )
                style.Append(String.Format("width:{0}px;", (int)_MaxWidth));
            if( _MaxHeight > 0 )
                style.Append(String.Format("height:{0}px;", (int)_MaxHeight));
            if( ( _MaxWidth == 0 ) && ( _MaxHeight == 0 ) )
                style.Append("width:99.5%;");

            style.Append("\">\r\n");

            res.Insert(0, style.ToString());
            res.Append("</div>");

            // Insert copyright string:
            res.Insert(0, beginstring);
            res.Append(endstring);

            return res.ToString();
        }

        private string ConvertHtmlColors(string[] colors)
        {
            Color clr = System.Drawing.Color.FromArgb(
                Convert.ToInt32(colors[1]),
                Convert.ToInt32(colors[2]),
                Convert.ToInt32(colors[3]));
            return "#" + clr.ToArgb().ToString("x").Substring(2);
        }


    }
}
